'''

    This module defines the base model to use to have
    event history on you models.
    
    Classes defined:
        Event
            This model defines the history entries.
        
        Historyfied
            Subclass this one. Read the constructor doc to
            find out why and how you must use the decorator
            @Hisotryfied.BindEvents() on subclasses.
        
'''
import re

from django.core.validators import RegexValidator
#from django.core.exceptions import ValidationError
from django.db import models
from django.db.models.signals import pre_save
#from django.dispatch import receiver
from django.contrib.auth.models import User


class Event(models.Model):
    '''
    Events threads give the hisoty of Historyfied subclasses objects.
    '''
    date = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(User)
    model = models.CharField(max_length=16)
    model_id = models.IntegerField()
    model_field = models.CharField(max_length=32, default='_All_')
    previous_value = models.CharField(max_length=256, default='_None_')
    new_value = models.CharField(max_length=256)
    description = models.CharField(max_length=128, default='Creation')
    previous_event = models.ForeignKey('self', null=True, blank=True)
    
    def __unicode__(self):
        return self.description


class Historyfied(models.Model):
    '''
    This base model ensures subclasses have the required fields
    and behavior to store all modifications in the Event table.
    
    It is used as a base for all models in the crypte.
    
    All sub class MUST be decorated with the @Historyfied.BindEvents decorator.
    See Historyfied.BindEvents's doc.
    
    '''
    name_validator = RegexValidator(
        re.compile(r'^[a-zA-Z][a-zA-Z0-9]+$'),
        "Bad name, must start with a letter and use only letters and digits",
    )
    name = models.CharField(max_length=32, validators=[name_validator])
    
    create_date = models.DateTimeField(auto_now_add=True)
    edit_date = models.DateTimeField(auto_now=True)
    last_event = models.ForeignKey(Event, null=True, blank=True)
    
    def _field_names(self):
        return [ f.name for f in self._meta.fields ]
    
    @classmethod
    def _pre_save(klass, sender, instance, using, **kwargs):
        '''
        Pre save hook for Historyfied and subclass instances.
        Will compute the difference between the instance to save and the db state
        and create an Event entry for each of them in order to set the instance's
        last_event field.
        '''
        print "########## Historyfied PRE-SAVE on", str(instance), `instance.pk`
        
        if instance.pk == None:
            print '    Creation Time, no diff to find out.'
            return
            
        previous = instance.__class__.objects.get(pk=instance.pk)
            
        diffs = []
        for fieldName in instance._field_names():
            print '   %20s' % fieldName, 
            if fieldName.startswith('_') or fieldName.endswith('_ptr'):
                print 'skipped'
                continue
            try:
                pv = getattr(previous,fieldName)
            except AttributeError, err:
                print err
                continue
            iv = getattr(instance,fieldName)
            
            if pv != iv:
                description = "Changing "+sender.__name__+"'s "+fieldName+' from "'+str(pv)+'" to "'+str(iv)+'"'
                print description
                diffs.append(
                    {
                        'model_field':fieldName, 
                        'previous_value':pv,
                        'new_value':iv,
                        'description':description,
                    }
                )
            else:
                print "untouched"
                
        last_event = previous.last_event
        for diff in diffs:
            e = Event(
                    user=User.objects.get(pk=1),                # TODO: get current user
                    model=instance.__class__.__name__,
                    model_id=instance.pk,
                    previous_event=last_event,
                    **diff
                )
            e.save()
            last_event = e
        instance.last_event = last_event
    
    @staticmethod
    def BindEvents(klass):
        '''
        This decorator must be used on every subclasses to propagate the events
        to ancestor classes. This is needed because we filter events on the class name
        (Which changes when subclassing :p)
        
        The subclass may override the _pre_save class method if it calls its super class'
        implementation in its own implementation:
            @classmethod
            def _pre_save(klass, sender, instance, using, **kwargs):
                super(MySubClass, klass)._pre_save(sender, instance, using, **kwargs)
                # do my stuffs...
                
        '''
        if not issubclass(klass, Historyfied):
            raise Exception(
                "Bad decorator usage on "+`klass`+", allowed on Historyfied sub classes only."
            )
        pre_save.connect(klass._pre_save, sender=klass, dispatch_uid=klass.__name__+"._pre_save")
        return klass
